package org.p2psimulator.entity;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;

import org.p2psimulator.entity.behavior.Behavior;
import org.p2psimulator.entity.protocol.Protocol;
import org.p2psimulator.entity.protocol.messages.MessageTypes;
import org.p2psimulator.events.Event;
import org.p2psimulator.events.EventHandler;
import org.p2psimulator.factory.idfactory.TransactionIdFactory;

@SuppressWarnings("unchecked")
public class Peer extends EventHandler {

    private int x;
    private int y;
    private Id id;
    private static TransactionIdFactory idFactory = new TransactionIdFactory();
    private Protocol protocol;
    private Behavior behavior;
    private boolean online = false;
    // armazena as mensagens enviadas para este peer
    private Map<String, List<Message>> messageStack;
    // armazena as informações de caminho para cada mensagem repassada
    private Map<Integer, Id> routingMessagesMap;
    private Set<String> processedMessages;

    public Id getId() {
        return id;
    }

    public void setId(Id id) {
        this.id = id;
    }

    public void handleEvent(Event evt) {
        Message msg = (Message) evt;
        if (isActive()) {
            if (protocol.getCurrentTransactioId() == msg.getTransactionId()) {
                addMessageToStack(msg);
            } else {
                this.protocol.answareMessage(this, msg);
            }
        } else {
            this.protocol.answareMessage(this, msg);
//            if (msg.getName().equalsIgnoreCase(MessageTypes.DOWNLOAD_ANSWARE_MESSAGE.getName())){
//                    System.out.println("EEEEEHHHHHH  MSG ID: " + msg.getDestination().getValue() + 
//                            " PEER ID: " + this.id.getValue());
//             }
        }
    }

    public Peer(Id id, Protocol protocol, Behavior behavior) {
        super();
        this.id = id;
        this.protocol = protocol;
        this.behavior = behavior;

        this.messageStack = new HashMap<String, List<Message>>();
        for (MessageTypes type : MessageTypes.values()) {
            this.messageStack.put(type.getName(), new ArrayList<Message> ());
        }
        this.routingMessagesMap = new HashMap<Integer, Id>();
        this.processedMessages = new HashSet<String>();
    }

    public Behavior getBehavior() {
        return behavior;
    }

    public void setBehavior(Behavior behavior) {
        this.behavior = behavior;
    }

    public Protocol getProtocol() {
        return protocol;
    }

    public void setProtocol(Protocol protocol) {
        this.protocol = protocol;
    }

    public void addMessageToStack(Message msg) {

        synchronized (messageStack) {
            messageStack.get(msg.getName()).add(msg);
        }
    }

    public Map<String, List<Message>> getMessageStack() {
        return messageStack;
    }

    public void setMessageStack(Map<String, List<Message>> messageStack) {
        this.messageStack = messageStack;
    }

    public Map<Integer, Id> getRoutingMessagesMap() {
        return routingMessagesMap;
    }

    public void setRoutingMessagesMap(Map<Integer, Id> routingMessagesMap) {
        this.routingMessagesMap = routingMessagesMap;
    }

    public void activate() {

        int trans = Math.abs(new Random().nextInt());

        this.protocol.setCurrentTransactioId(trans);
    }

    public void deActivate() {
        this.protocol.setCurrentTransactioId(-1);
    }

    public boolean isActive() {
        return this.protocol.getCurrentTransactioId() != -1;
    }

    public void cleanUp() {

        synchronized (routingMessagesMap) {
            this.routingMessagesMap.clear();
        }
        synchronized (messageStack) {
            this.messageStack.clear();
            for (MessageTypes type : MessageTypes.values()) {
                this.messageStack.put(type.getName(), new ArrayList<Message>());
            }
        }
        synchronized (processedMessages) {
            this.processedMessages.clear();
        }

    }

    public static TransactionIdFactory getIdFactory() {
        return idFactory;
    }

    public static void setIdFactory(TransactionIdFactory idFactory) {
        Peer.idFactory = idFactory;
    }

    public boolean isOnline() {
        return online;
    }

    public void setOnline(boolean insideNetwork) {
        this.online = insideNetwork;
    }

    public String toString() {
        return "" + this.id;
    }

    public Set<String> getProcessedMessages() {
        return processedMessages;
    }

    public void setProcessedMessages(Set<String> processedMessages) {
        this.processedMessages = processedMessages;
    }

    public int getX() {
        return x;
    }

    public void setX(int x) {
        this.x = x;
    }

    public int getY() {
        return y;
    }

    public void setY(int y) {
        this.y = y;
    }
}
